/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

import {Live2DCubismFramework as cubismvector2} from '../math/cubismvector2';
import {Live2DCubismFramework as cubismid} from '../id/cubismid';
import {Live2DCubismFramework as csmvector} from '../type/csmvector';
import csmVector = csmvector.csmVector;
import CubismIdHandle = cubismid.CubismIdHandle;
import CubismVector2 = cubismvector2.CubismVector2;

export namespace Live2DCubismFramework
{
    /**
     * 物理演算の適用先の種類
     */
    export enum CubismPhysicsTargetType
    {
        CubismPhysicsTargetType_Parameter,  // パラメータに対して適用
    }

    /**
     * 物理演算の入力の種類
     */
    export enum CubismPhysicsSource
    {
        CubismPhysicsSource_X,          // X軸の位置から
        CubismPhysicsSource_Y,          // Y軸の位置から
        CubismPhysicsSource_Angle,      // 角度から
    }

    /**
     * @brief 物理演算で使用する外部の力
     *
     * 物理演算で使用する外部の力。
     */
    export class PhysicsJsonEffectiveForces
    {
        constructor()
        {
            this.gravity = new CubismVector2(0, 0);
            this.wind = new CubismVector2(0, 0);
        }
        gravity:    CubismVector2;          ///< 重力
        wind:       CubismVector2;          ///< 風
    }

    /**
     * 物理演算のパラメータ情報
     */
    export class CubismPhysicsParameter
    {
        id: CubismIdHandle;   // パラメータ
        targetType: CubismPhysicsTargetType;    // 適用先の種類
    }

    /**
     * 物理演算の正規化情報
     */
    export class CubismPhysicsNormalization
    {
        minimum: number;    // 最大値
        maximum: number;    // 最小値
        defalut: number;    // デフォルト値
    }

    /**
     * 物理演算の演算委使用する物理点の情報
     */
    export class CubismPhysicsParticle
    {
        constructor()
        {
            this.initialPosition = new CubismVector2(0, 0);
            this.position = new CubismVector2(0, 0);
            this.lastPosition = new CubismVector2(0, 0);
            this.lastGravity = new CubismVector2(0, 0);
            this.force = new CubismVector2(0, 0);
            this.velocity = new CubismVector2(0, 0);
        }

        initialPosition: CubismVector2; // 初期位置
        mobility: number;               // 動きやすさ
        delay: number;                  // 遅れ
        acceleration: number;           // 加速度
        radius: number;                 // 距離
        position: CubismVector2;        // 現在の位置
        lastPosition: CubismVector2;    // 最後の位置
        lastGravity: CubismVector2;     // 最後の重力
        force: CubismVector2;           // 現在かかっている力
        velocity: CubismVector2;        // 現在の速度
    }

    /**
     * 物理演算の物理点の管理
     */
    export class CubismPhysicsSubRig
    {
        constructor()
        {
            this.normalizationPosition = new CubismPhysicsNormalization();
            this.normalizationAngle = new CubismPhysicsNormalization();
        }
        inputCount: number;                                 // 入力の個数
        outputCount: number;                                // 出力の個数
        particleCount: number;                              // 物理点の個数
        baseInputIndex: number;                             // 入力の最初のインデックス
        baseOutputIndex: number;                            // 出力の最初のインデックス
        baseParticleIndex: number;                          // 物理点の最初のインデックス
        normalizationPosition: CubismPhysicsNormalization;  // 正規化された位置
        normalizationAngle: CubismPhysicsNormalization;     // 正規化された角度
    }

    /**
     * 正規化されたパラメータの取得関数の宣言
     * @param targetTranslation     // 演算結果の移動値
     * @param targetAngle           // 演算結果の角度
     * @param value                 // パラメータの値
     * @param parameterMinimunValue // パラメータの最小値
     * @param parameterMaximumValue // パラメータの最大値
     * @param parameterDefaultValue // パラメータのデフォルト値
     * @param normalizationPosition // 正規化された位置
     * @param normalizationAngle    // 正規化された角度
     * @param isInverted            // 値が反転されているか？
     * @param weight                // 重み
     */
    export interface normalizedPhysicsParameterValueGetter
    {
        (
            targetTranslation: CubismVector2,
            targetAngle: {angle: number},
            value: number,
            parameterMinimunValue: number,
            parameterMaximumValue: number,
            parameterDefaultValue: number,
            normalizationPosition: CubismPhysicsNormalization,
            normalizationAngle: CubismPhysicsNormalization,
            isInverted: boolean,
            weight: number
        ): void
    }

    /**
     * 物理演算の値の取得関数の宣言
     * @param translation 移動値
     * @param particles 物理点のリスト
     * @param isInverted 値が反映されているか
     * @param parentGravity 重力
     * @return 値
     */
    export interface physicsValueGetter
    {
        (
            translation: CubismVector2,
            particles: CubismPhysicsParticle[],
            particleIndex: number,
            isInverted: boolean,
            parentGravity: CubismVector2
        ): number
    }

    /**
     * 物理演算のスケールの取得関数の宣言
     * @param translationScale 移動値のスケール
     * @param angleScale    角度のスケール
     * @return スケール値
     */
    export interface physicsScaleGetter
    {
        (
            translationScale: CubismVector2,
            angleScale: number
        ): number;
    }

    /**
     * 物理演算の入力情報
     */
    export class CubismPhysicsInput
    {
        constructor()
        {
            this.source = new CubismPhysicsParameter();
        }
        source: CubismPhysicsParameter;     // 入力元のパラメータ
        sourceParameterIndex: number;       // 入力元のパラメータのインデックス
        weight: number;                     // 重み
        type: number;                       // 入力の種類
        reflect: boolean;                   // 値が反転されているかどうか
        getNormalizedParameterValue: normalizedPhysicsParameterValueGetter;   // 正規化されたパラメータ値の取得関数
    }

    /**
     * @brief 物理演算の出力情報
     *
     * 物理演算の出力情報。
     */
    export class CubismPhysicsOutput
    {
        constructor()
        {
            this.destination = new CubismPhysicsParameter();
            this.translationScale = new CubismVector2(0, 0);
        }

        destination: CubismPhysicsParameter;        ///< 出力先のパラメータ
        destinationParameterIndex: number;          ///< 出力先のパラメータのインデックス
        vertexIndex: number;                        ///< 振り子のインデックス
        translationScale: CubismVector2;            ///< 移動値のスケール
        angleScale: number;                         ///< 角度のスケール
        weight: number;                             /// 重み
        type: CubismPhysicsSource;                  ///< 出力の種類
        reflect: boolean;                           ///< 値が反転されているかどうか
        valueBelowMinimum: number;                  ///< 最小値を下回った時の値
        valueExceededMaximum: number;               ///< 最大値をこえた時の値
        getValue: physicsValueGetter;             ///< 物理演算の値の取得関数
        getScale: physicsScaleGetter;             ///< 物理演算のスケール値の取得関数
    }

    /**
     * @brief 物理演算のデータ
     *
     * 物理演算のデータ。
     */
    export class CubismPhysicsRig
    {
        constructor()
        {
            this.settings = new csmVector<CubismPhysicsSubRig>();
            this.inputs = new csmVector<CubismPhysicsInput>();
            this.outputs = new csmVector<CubismPhysicsOutput>();
            this.particles = new csmVector<CubismPhysicsParticle>();
            this.gravity = new CubismVector2(0, 0);
            this.wind = new CubismVector2(0, 0);
        }

        subRigCount: number;                    ///< 物理演算の物理点の個数
        settings: csmVector<CubismPhysicsSubRig>;        ///< 物理演算の物理点の管理のリスト
        inputs: csmVector<CubismPhysicsInput>;           ///< 物理演算の入力のリスト
        outputs: csmVector<CubismPhysicsOutput>;         ///< 物理演算の出力のリスト
        particles: csmVector<CubismPhysicsParticle>;     ///< 物理演算の物理点のリスト
        gravity: CubismVector2;                 ///< 重力
        wind: CubismVector2;                    ///< 風
    };
}
